package com.itheima.aduit.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.aduit.mapper2.ProInstanceMapper;
import com.itheima.aduit.mapper2.ProcTaskInstanceMapper;
import com.itheima.aduit.mapper2.ProcUserGroupMapper;
import com.itheima.aduit.mapper2.UserMapper;
import com.itheima.aduit.pojo.*;
import com.itheima.aduit.service.AduitService;
import com.itheima.common.entity.PageResult;
import com.itheima.common.utils.BeanMapUtils;
import com.itheima.common.utils.IdWorker;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
 * @author qiankaofei
 * @version 1.0
 * @date 2020/12/22 21:01
 */
@Service
@SuppressWarnings(value = "all")
public class AduitServiceImpl implements AduitService {

    @Autowired
    private ProInstanceMapper proInstanceMapper;

    @Autowired
    private ProcTaskInstanceMapper procTaskInstanceMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProcUserGroupMapper procUserGroupMapper;

    /**
     * 根据id查询流程实例
     */
    @Override
    public ProcInstance findInstanceById(String id) {
        QueryWrapper<ProcInstance> procInstanceQuery=new QueryWrapper<ProcInstance>();
        procInstanceQuery.eq("process_id",id);
        ProcInstance procInstance = proInstanceMapper.selectOne(procInstanceQuery);
        if(procInstance==null){
            return null;
        }
        return procInstance;
    }

    /**
     * 查询申请列表
     *  参数:
     *      page,size
     *  业务参数:
     *      审批类型
     *      审批状态(多个,每个状态之间使用","隔开)
     *      当前节点的待处理人
     */
    @Override
    public PageResult<ProcInstance> instanceList(Integer page, Integer size, ProcInstance instance,String companyId) {
        //审批状态
        List<String> processStateList=new ArrayList<String>(0);
        if(StringUtils.isNotBlank(instance.getProcessState())){
            processStateList= Arrays.asList(instance.getProcessState().split(","));
        }
        //查询
        List<ProcInstance> procInstanceList=proInstanceMapper.findInstanceList(instance.getProcessKey(), processStateList,instance.getProcCurrNodeUserId(),instance.getUserId(),(page-1)*size,size,companyId);
        return new PageResult<ProcInstance>(proInstanceMapper.getCountInstanceList(companyId,instance.getProcessKey(),processStateList,instance.getProcCurrNodeUserId(),instance.getUserId()),procInstanceList);
    }

    /**
     * 流程申请    以请假申请流程为例
     * 构造业务数据
     * 查询流程定义
     * 开启流程
     * 自动执行第一个任务节点
     * 获取下一个节点数据，填充业务数据中当前待审批人
     *
     *
     *  三张业务表的结构如下：
     * CREATE TABLE `proc_instance` (
     *   `process_id` varchar(45) NOT NULL COMMENT '流程实例ID',
     *   `process_key` varchar(45) DEFAULT NULL COMMENT '流程标识',
     *   `process_name` varchar(45) DEFAULT NULL COMMENT '流程名称',
     *   `process_definition_id` varchar(100) DEFAULT NULL COMMENT '流程定义ID',
     *   `process_state` varchar(3) DEFAULT NULL COMMENT '流程状态（0已提交；1审批中；2审批通过；3审批不通过；4撤销）',
     *   `user_id` varchar(45) DEFAULT NULL COMMENT '申请人ID',
     *   `username` varchar(50) DEFAULT NULL COMMENT '申请人',
     *   `proc_apply_time` datetime DEFAULT NULL COMMENT '申请时间',
     *   `proc_curr_node_user_id` varchar(255) DEFAULT NULL COMMENT '当前节点审批人ID',
     *   `proc_curr_node_user_name` varchar(255) DEFAULT NULL COMMENT '当前节点审批人',
     *   `proc_end_time` datetime DEFAULT NULL COMMENT '结束流程时间',
     *   `proc_data` longtext,
     *   `department_id` varchar(40) DEFAULT NULL,
     *   `department_name` varchar(40) DEFAULT NULL,
     *   `time_of_entry` datetime DEFAULT NULL,
     *   `company_id` varchar(64) DEFAULT NULL,
     *   PRIMARY KEY (`process_id`)
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8
     *
     *
     *CREATE TABLE `proc_task_instance` (
     *   `process_id` varchar(45) DEFAULT NULL COMMENT '流程实例ID',
     *   `task_id` varchar(45) NOT NULL COMMENT '任务实例ID',
     *   `task_key` varchar(45) DEFAULT NULL COMMENT '任务节点key',
     *   `task_name` varchar(45) DEFAULT NULL COMMENT '任务节点',
     *   `should_user_id` varchar(45) DEFAULT NULL COMMENT '应审批用户ID',
     *   `should_user_name` varchar(2000) DEFAULT NULL COMMENT '应审批用户',
     *   `handle_user_id` varchar(2000) DEFAULT NULL COMMENT '实际处理用户ID',
     *   `handle_user_name` varchar(45) DEFAULT NULL COMMENT '实际处理用户',
     *   `handle_time` datetime DEFAULT NULL COMMENT '处理时间',
     *   `handle_opinion` varchar(45) DEFAULT NULL COMMENT '处理意见',
     *   `handle_type` varchar(45) DEFAULT NULL COMMENT '处理类型（2审批通过；3审批不通过；4撤销）',
     *   PRIMARY KEY (`task_id`)
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8
     *
     *
     * CREATE TABLE `proc_user_group` (
     *   `id` varchar(45) NOT NULL COMMENT '主键',
     *   `name` varchar(100) DEFAULT NULL COMMENT '组名',
     *   `param` varchar(45) DEFAULT NULL COMMENT '入参',
     *   `isql` varchar(1000) DEFAULT NULL COMMENT '对应sql',
     *   `isvalid` varchar(2) DEFAULT NULL COMMENT '有效标记',
     *   `create_user` varchar(45) DEFAULT NULL COMMENT '创建人',
     *   `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
     *   `update_user` varchar(45) DEFAULT NULL COMMENT '最后更新人',
     *   `update_time` timestamp NULL DEFAULT NULL COMMENT '最后更新时间',
     *   PRIMARY KEY (`id`)
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8
     *
     *
     *
     *
     */
    @Override
    @Transactional
    public void startProcess(QueryParam queryParam, String companyId){
        //从queryParam中获取processKey prcocessName  userId
        String processKey = queryParam.getProcessKey();
        String processName = queryParam.getProcessName();
        String userId = queryParam.getUserId();
        //根据userId 查询用户信息
        User user = userMapper.selectById(userId);
        //查询流程定义
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processKey)
                .processDefinitionTenantId(companyId)
                .singleResult();
        //构造ProcInstance属性值
        ProcInstance procInstance=new ProcInstance();
        BeanUtils.copyProperties(user,procInstance);
        procInstance.setProcessId(String.valueOf(idWorker.nextId()));
        procInstance.setProcessDefinitionId(definition.getId());
        procInstance.setProcessKey(processKey);
        procInstance.setProcessName(processName);
        procInstance.setProcessState("1");
        procInstance.setProcApplyTime(new Date());
        procInstance.setProcData(JSON.toJSONString(BeanMapUtils.beanToMap(queryParam)));
        //开启流程
        Map<String,Object> dataMap=new HashMap<String,Object>(0);
        if(StringUtils.equals("process_leave",queryParam.getProcessKey())){
            dataMap.put("days",queryParam.getDuration());
        }
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(definition.getId(), dataMap);

        //自己提交的任务自己完成
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        if(task!=null){
            taskService.complete(task.getId());
        }
        //查询下一个节点的审批人，填充页面上待审批人的数据
        Task next = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        if(next!=null){
            //获取下一个节点的候选人信息（userIds   userNames）
            StringBuilder userIds=new StringBuilder();
            StringBuilder userNames=new StringBuilder();
            List<User> currentUsers = this.findCurrentUsers(next, user);
            for (User currentUser : currentUsers) {
                userIds.append(currentUser.getId()).append("");
                userIds.append(currentUser.getUsername()).append("");
            }
            //procInstance中赋值   procCurrNodeUserId  procCurrNodeUserName
            procInstance.setProcCurrNodeUserId(userIds.toString());
            procInstance.setProcCurrNodeUserName(userNames.toString());
        }

        //构造ProcTaskInstance数据
        ProcTaskInstance procTaskInstance=new ProcTaskInstance();
        procTaskInstance.setTaskId(String.valueOf(idWorker.nextId()));
        procTaskInstance.setHandleOpinion("同意!");
        procTaskInstance.setHandleTime(new Date());
        procTaskInstance.setHandleType("2");
        procTaskInstance.setHandleUserId(userId);
        procTaskInstance.setHandleUserName(user.getUsername());
        procTaskInstance.setProcessId(procInstance.getProcessId());
        procTaskInstance.setTaskKey(task.getTaskDefinitionKey());
        procTaskInstance.setTaskName(task.getName());

        // 数据入库保存
        proInstanceMapper.insert(procInstance);
        procTaskInstanceMapper.insert(procTaskInstance);
    }

    /**
     * 查询当前节点的候选人（审批人）
     * @param next
     * @param user
     * @return
     */
    private List<User> findCurrentUsers(Task next, User user) {
        //返回的结果数据


        List<User> userList=new ArrayList<User>(0);
        List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(next.getId());
        for (IdentityLink identityLink : identityLinksForTask) {
            //获取identityLink中的groupId
            String groupId = identityLink.getGroupId();
            //根据groupId 查询工作流业务数据表  proc_user_group
            ProcUserGroup procUserGroup = procUserGroupMapper.selectById(groupId);
            String paramValue="";
            if(StringUtils.equals("user_id",procUserGroup.getParam())){
                paramValue=user.getId();
            }else if(StringUtils.equals("company_id",procUserGroup.getParam())){
                paramValue=user.getCompanyId();
            }else if(StringUtils.equals("department_id",procUserGroup.getParam())){
                paramValue=user.getDepartmentId();
            }
            String sql=procUserGroup.getIsql().replaceAll("\\$\\{" + procUserGroup.getParam() + "\\}",paramValue);
            List<User> userListByParams = userMapper.findUserListByParams(sql);
            userList.addAll(userListByParams);
        }
        return userList;
    }

    /**
     * 流程的提交申请     自己撤销  审核人的驳回，通过
     */
    @Override
    public void commit(ProcTaskInstance procTaskInstance, String companyId) {
        //根据processInstanceId 查询业务数据中的表 proc_instance
        QueryWrapper<ProcInstance> queryWrapper=new QueryWrapper<ProcInstance>();
        queryWrapper.eq("process_id",procTaskInstance.getProcessId());
        ProcInstance procInstance = proInstanceMapper.selectOne(queryWrapper);
        //查询流程实例（activiti数据库中的表）
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(procTaskInstance.getProcessId())
                .singleResult();
        //查询用户
        User user = userMapper.selectById(procTaskInstance.getHandleUserId());
        //流程的驳回，撤销，通过
        if(StringUtils.equals(procTaskInstance.getHandleType(),"2")){
            //审批通过
            //自己的任务自己完成
            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
            if(task!=null){
                taskService.complete(task.getId());
            }
            //获取下一个节点
            Task next = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
            if(next!=null){
                //获取下一个节点的候选人信息（userIds   userNames）
                StringBuilder userIds=new StringBuilder();
                StringBuilder userNames=new StringBuilder();
                List<User> currentUsers = this.findCurrentUsers(next, user);
                for (User currentUser : currentUsers) {
                    userIds.append(currentUser.getId()).append("");
                    userIds.append(currentUser.getUsername()).append("");
                }
                //procInstance中赋值   procCurrNodeUserId  procCurrNodeUserName
                procInstance.setProcCurrNodeUserId(userIds.toString());
                procInstance.setProcCurrNodeUserName(userNames.toString());

            }else{
                //不存在下一个节点，任务结束
                procInstance.setProcessState("2");
                procInstance.setProcEndTime(new Date());
            }
        }else{
            //审批不通过 （撤销，驳回）   状态统一以不通过更新入库
            //删除(删除任务)
            runtimeService.deleteProcessInstance(processInstance.getId(),procTaskInstance.getHandleOpinion());
            procInstance.setProcessState("3");    //不通过
            procInstance.setProcEndTime(new Date());
        }
        procInstance.setUsername(user.getUsername());
        //修改proc_instance表
        proInstanceMapper.update(procInstance,queryWrapper);
        //新增proc_task_instance表
        procTaskInstance.setTaskId(String.valueOf(idWorker.nextId()));
        procTaskInstance.setHandleUserName(user.getUsername());
        procTaskInstance.setHandleTime(new Date());
        procTaskInstanceMapper.insert(procTaskInstance);
    }
}
